#include "modbus_ui.h"
#include <QDebug>
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
#include <QModbusDataUnit>
#include <QModbusTcpClient>
#include <QModbusRtuSerialMaster>
#include <iostream>

ModbusInterface::ModbusInterface()
{
    qRegisterMetaType<QVariant>("QVariant");
    modbusDevice = new QModbusRtuSerialMaster();
    m_stationNum = 1;
    m_timerModbusDelay = new QTimer();
    m_flagModbusWork = false;
    m_vecReadBuf.insert(0, _MODBUS_READ_STEP+2, 0);
    m_byteArraySendData.resize(_MODBUS_READ_STEP*2+4);
}

ModbusInterface::~ModbusInterface()
{
    if (modbusDevice)
        modbusDevice->disconnectDevice();
    delete modbusDevice;
}

void ModbusInterface::SL_modbusRead(int addr, quint16 amount, int delayTime)
{
    QVector<quint16> t_vec;
    struct _modbusWorkStruct t_mws={
        .readOrNot = true,
                .addr = addr,
                .delayTime = delayTime,
                .amount = amount,
                .vec = t_vec
    };
    m_queueModbusWork.push_back(t_mws);
    SN_queuePush();
    if(m_flagModbusWork == false){
        modbusStart();
    }
}

void ModbusInterface::SL_modbusWrite(int addr, quint16 data, int delayTime)
{
    QVector <quint16> t_vec;
    t_vec.push_back(data);

    if(addr>=0x4000) addr += 355;
    struct _modbusWorkStruct t_mws={
        .readOrNot = false,
                .addr = addr,
                .delayTime = delayTime,
                .amount = static_cast<quint16>(t_vec.size()),
                .vec = t_vec
    };
    m_queueModbusWork.push_back(t_mws);
    SN_queuePush();
    if(m_flagModbusWork == false)
        modbusStart();
    //
    if(addr>=0x4163)
    {
        QTimer *timer = new QTimer;
        struct _modbusWorkStruct t_mwsr={
            .readOrNot = true,
                    .addr = addr,
                    .delayTime = delayTime,
                    .amount = t_mws.amount,
                    .vec = t_vec
        };
        timer->singleShot(1000,this,[this,t_mwsr](){
            m_queueModbusWork.push_back(t_mwsr);
            SN_queuePush();
            if(m_flagModbusWork == false)
                modbusStart();
        });
    }
}

void ModbusInterface::SL_modbusWriteArray(int addr, QVector<quint16> data, int delayTime)
{
    if(addr>=0x4000) addr += 355;
    struct _modbusWorkStruct t_mws={
        .readOrNot = false,
                .addr = addr,
                .delayTime = delayTime,
                .amount = static_cast<quint16>(data.size()),
                .vec = data
    };
    m_queueModbusWork.push_back(t_mws);
    SN_queuePush();
    if(m_flagModbusWork == false)
        modbusStart();
    //
    if(addr>=0x4163)
    {
        QTimer *timer = new QTimer;
        struct _modbusWorkStruct t_mwsr={
            .readOrNot = true,
                    .addr = addr,
                    .delayTime = delayTime,
                    .amount = t_mws.amount,
                    .vec = data
        };
        timer->singleShot(1000,this,[this,t_mwsr](){
            m_queueModbusWork.push_back(t_mwsr);
            SN_queuePush();
            if(m_flagModbusWork == false)
                modbusStart();
        });
    }
}


void ModbusInterface::modbusStart()
{
#ifdef _MODBUS_DEBUG_PRINT_
    qDebug() << "modbusStart children thread:" << this->currentThreadId();
    qDebug() << "once" ;
//    qDebug() << "modbusStart children thread:" << this->currentThreadId();
    std::cout << "modbusStart children thread:" << std::this_thread::get_id()<< std::endl;
#endif
    //modbus disconnect error
    if (modbusDevice->state() != QModbusDevice::ConnectedState)
    {
        m_queueModbusWork.clear();
        SN_queuePop();
        SN_printMsg(3,tr("modbus disconnect"));
        m_flagModbusWork = false;
    }

    //queue emtpy error
    if(m_queueModbusWork.isEmpty()){
        m_flagModbusWork = false;
        return;
    }
    m_flagModbusWork = true;
    struct _modbusWorkStruct t_mws = m_queueModbusWork.head();
    m_queueModbusWork.pop_front();
    SN_queuePop();

    //read
    if(t_mws.readOrNot==true)
    {
        //记录开始时时间
        struct timeval tpstart;
        gettimeofday(&tpstart,NULL);
#ifdef _MODBUS_DISPLAY
        SN_printMsg(1,QString("------->read time start:"+QString::number(tpstart.tv_sec) + "s " + QString::number(tpstart.tv_usec)+ "us"));
#endif

        //read
        if (auto *reply = modbusDevice->sendReadRequest(
                    QModbusDataUnit(static_cast<QModbusDataUnit::RegisterType>(QModbusDataUnit::RegisterType::HoldingRegisters),
                                    t_mws.addr,
                                    t_mws.amount),
                    m_stationNum)) {
            if (!reply->isFinished())
                connect(reply, &QModbusReply::finished, this, [t_mws, this, reply](){

#ifdef _MODBUS_DEBUG_PRINT_
    std::cout << "recall children thread:" << std::this_thread::get_id()<< std::endl;
    qDebug() << this->currentThreadId();
#endif
                    //uname recall function
                    //handle
                    //m_byteArraySendData格式:006300300000....
                    if (reply->error() == QModbusDevice::NoError) {
                        const QModbusDataUnit unit = reply->result();
                        QString entry;

                        //专门为reg的地址提供355（0x163）的数据转化
                        quint16 t_addrTrams = (quint16)unit.startAddress();
                        if(t_addrTrams>(0x4100+355)) t_addrTrams-=355;

                        m_byteArraySendData[0] = t_addrTrams&0xFF;
                        m_byteArraySendData[1] = t_addrTrams>>8;
                        m_byteArraySendData[2] = (quint16)unit.valueCount()&0xFF;
                        m_byteArraySendData[3] = (quint16)unit.valueCount()>>8;
#ifdef _MODBUS_DISPLAY
                        m_vecReadBuf[0] = (quint16)unit.startAddress();
                        m_vecReadBuf[1] = (quint16)unit.valueCount();
#endif
                        for (uint i = 0; i < unit.valueCount(); i++) {
                            quint16 t_addr = unit.startAddress() + i;
                            entry = entry+tr("0x%1:%2 ").arg(QString::number(t_addr,16))
                                    .arg(QString::number(unit.value(i),
                                                         unit.registerType() <= QModbusDataUnit::Coils ? 10 : 16));
                            //送到缓冲里面
                            m_byteArraySendData[2*i+4]=unit.value(i)&0xFF;
                            m_byteArraySendData[2*i+5]=unit.value(i)>>8;

#ifdef _MODBUS_DISPLAY
                            m_vecReadBuf[i+2] = unit.value(i);
#endif
                        }
#ifdef _MODBUS_DISPLAY
                        SN_printMsg(1,QString(entry));
                        //记录结束时间
                        struct timeval tpstart;
                        gettimeofday(&tpstart,NULL);
                        SN_printMsg(1,QString("------->read time stop:"+QString::number(tpstart.tv_sec) + "s " + QString::number(tpstart.tv_usec)+ "us"));
#endif
                        //如果获取完一轮就打印出来,并且emit接收完结信号
                        m_byteArraySendData.resize((m_byteArraySendData[2]|m_byteArraySendData[3]<<8)*2+4);
                        SN_readFinsih(m_byteArraySendData);
#ifdef _MODBUS_DISPLAY
                        QString t_str;
                        t_str = "addr: 0x" + QString::number(m_vecReadBuf.at(0),16) + "--"
                                +  QString::number(m_vecReadBuf.at(0)+m_vecReadBuf.at(1)-1,16) + "\r\n";
                        for (quint16 t_uint=2; t_uint<m_vecReadBuf.at(1)+2; t_uint++)
                            t_str=t_str+"0x"+QString::number(m_vecReadBuf.at(t_uint),16)+"\t";
                        t_str = t_str+"\r\n";
                        SN_printMsg(2,QString(t_str));
#endif

                    }
                    else if (reply->error() == QModbusDevice::ProtocolError) {
                        SN_printMsg(3,tr("Read response error: %1 (Mobus exception: 0x%2)").
                                                 arg(reply->errorString()).
                                                 arg(reply->rawResult().exceptionCode(), -1, 16));
                        m_queueModbusWork.clear();
                        SN_queuePop();
                        SN_modbusError(reply->error());
                    }
                    else {
                        SN_printMsg(3,tr("Read response error: %1 (code: 0x%2)").
                                                 arg(reply->errorString()).
                                                 arg(reply->error(), -1, 16));
                        m_queueModbusWork.clear();
                        SN_queuePop();
                        SN_modbusError(reply->error());
                    }
                    reply->deleteLater();
#ifdef _MODBUS_DEBUG_PRINT_
                    qDebug() << "start m_timerModbusDelay";
#endif
                    m_timerModbusDelay->singleShot(t_mws.delayTime, this, [this](){
                        modbusStart();
#ifdef _MODBUS_DEBUG_PRINT_
                        qDebug() << "call modbusStart by read Timer";
#endif
                    });
                });
            else
                delete reply; // broadcast replies return immediately
        } else {
            SN_printMsg(3,tr("Read error: ") + modbusDevice->errorString());
        }
    }

    //write
    if(t_mws.readOrNot == false)
    {
        //记录开始时间
        struct timeval tpstart;
        gettimeofday(&tpstart,NULL);
#ifdef _MODBUS_DISPLAY
        SN_printMsg(1,QString("------->write time start:"+QString::number(tpstart.tv_sec) + "s " + QString::number(tpstart.tv_usec)+ "us"));
#endif
        QModbusDataUnit writeUnit = QModbusDataUnit(static_cast<QModbusDataUnit::RegisterType>(QModbusDataUnit::RegisterType::HoldingRegisters),
                                                    t_mws.addr, t_mws.vec.size());
        for(int i=0; i<t_mws.vec.size(); i++)
            writeUnit.setValue(i, t_mws.vec.at(i));
        if (auto *reply = modbusDevice->sendWriteRequest(writeUnit, m_stationNum)) {
            if (!reply->isFinished()) {
                connect(reply, &QModbusReply::finished, this, [t_mws, this, reply]() {

#ifdef _MODBUS_DEBUG_PRINT_
                    qDebug() << "twice";
    std::cout << "recall children thread:" << std::this_thread::get_id()<< std::endl;
    qDebug() << this->currentThreadId();
#endif
                    //记录结束时间
                    struct timeval tpstart;
                    gettimeofday(&tpstart,NULL);

#ifdef _MODBUS_DISPLAY
                    SN_printMsg(1,QString("------->write time stop:"+QString::number(tpstart.tv_sec) + "s " + QString::number(tpstart.tv_usec)+ "us"));                 
#endif
                    if (reply->error() == QModbusDevice::ProtocolError) {
                        SN_printMsg(3,tr("Write response error: %1 (Mobus exception: 0x%2)")
                                                 .arg(reply->errorString()).arg(reply->rawResult().exceptionCode(), -1, 16));
                        m_queueModbusWork.clear();
                        SN_queuePop();
                        SN_modbusError(reply->error());
                    }
                    else if (reply->error() != QModbusDevice::NoError) {
                        SN_printMsg(3,tr("Write response error: %1 (code: 0x%2)").
                                                 arg(reply->errorString()).arg(reply->error(), -1, 16));
                        m_queueModbusWork.clear();
                        SN_queuePop();
                        SN_modbusError(reply->error());
                    }



                    reply->deleteLater();
#ifdef _MODBUS_DEBUG_PRINT_
                    qDebug() << "start m_timerModbusDelay";
#endif
                    m_timerModbusDelay->singleShot(t_mws.delayTime, this, [this](){
                        modbusStart();
#ifdef _MODBUS_DEBUG_PRINT_
                        qDebug() << "call modbusStart by write Timer";
#endif
                    });

#ifdef _MODBUS_DEBUG_PRINT_
                    qDebug() << "twice-finish";
#endif
                });
            } else {
                // broadcast replies return immediately
                reply->deleteLater();
            }
        } else {
            SN_printMsg(3,tr("Write error: ") + modbusDevice->errorString());
        }

    }

#ifdef _MODBUS_DEBUG_PRINT_
    qDebug() << "once-finish";
#endif

}
